package com.lry.pda;

import java.util.Stack;

/**
 * 下推自动机
 */
public class PdaParser {

    enum Grammar {
        STMT,
        EXPR,
        EXPR_PRIME,
        TERM,
        TERM_PRIME,
        FACTOR,
        NUM_OR_ID,
        PLUS,
        SEMI,
        MULTIPLE,
        LEFT_PARENT,
        RIGHT_PARENT
    };

    //逆向将表达式压入栈中，遍历栈的时候发现栈顶是终结符，则终结符必须和lexer当前yytext匹配，若栈顶是非终结符，逆向将表达式压入栈中
    private Stack<Grammar> stack = new Stack<>();
    private Lexer lexer;

    public static void main(String[] args) {
        PdaParser pdaParser = new PdaParser(new Lexer());
        pdaParser.parse();
    }


    public PdaParser(Lexer lexer){
        this.lexer = lexer;
        this.lexer.input();
        this.lexer.advance();
        stack.push(Grammar.STMT);
    }

    public void parse(){
        while(!stack.isEmpty()){
            Grammar grammar = stack.peek();
            switch (grammar){

                //stmt -> expr semi stmt
                //        | 空
                case STMT:
                    //逆向压入表达式
                    stack.pop();
                    if(lexer.match(Lexer.EOI)){
                        break;
                    }
                    stack.push(Grammar.STMT);
                    stack.push(Grammar.SEMI);
                    stack.push(Grammar.EXPR);
                    break;

                //expr -> term exprPrime
                case EXPR:
                    stack.pop();
                    stack.push(Grammar.EXPR_PRIME);
                    stack.push(Grammar.TERM);
                    break;

                    //exprPrime -> plus term exprPrime
                    // | 空
                case EXPR_PRIME:
                    stack.pop();
                    if(!lexer.match(Lexer.PLUS)){
                        break;
                    }
                    stack.push(Grammar.EXPR_PRIME);
                    stack.push(Grammar.TERM);
                    stack.push(Grammar.PLUS);
                    break;

                    //term -> factor termPrime
                case TERM:
                    stack.pop();
                    stack.push(Grammar.TERM_PRIME);
                    stack.push(Grammar.FACTOR);
                    break;

                    //termPrime -> time factor termPrime
                    //| 空
                case TERM_PRIME:
                    stack.pop();
                    if(!lexer.match(Lexer.TIMES)){
                        break;
                    }
                    stack.push(Grammar.TERM_PRIME);
                    stack.push(Grammar.FACTOR);
                    stack.push(Grammar.MULTIPLE);
                    break;

                    //factor -> num
                    //| ( expr )
                case FACTOR:
                    stack.pop();
                    if(lexer.match(Lexer.NUM_OR_ID)){
                        stack.push(Grammar.NUM_OR_ID);
                    }else if(lexer.match(Lexer.LP)){
                        stack.push(Grammar.RIGHT_PARENT);
                        stack.push(Grammar.EXPR);
                        stack.push(Grammar.LEFT_PARENT);
                    }else {
                        parseError();
                    }
                    break;

                case NUM_OR_ID:
                    stack.pop();
                    if(!lexer.match(Lexer.NUM_OR_ID)){
                        parseError();
                    }
                    lexer.advance();
                    break;

                case PLUS:
                    stack.pop();
                    if(!lexer.match(Lexer.PLUS)){
                        parseError();
                    }
                    lexer.advance();
                    break;

                case SEMI:
                    stack.pop();
                    if(!lexer.match(Lexer.SEMI)){
                        parseError();
                    }
                    lexer.advance();
                    break;

                case MULTIPLE:
                    stack.pop();
                    if(!lexer.match(Lexer.TIMES)){
                        parseError();
                    }
                    lexer.advance();
                    break;

                case LEFT_PARENT:
                    stack.pop();
                    if(!lexer.match(Lexer.LP)){
                        parseError();
                    }
                    lexer.advance();
                    break;

                case RIGHT_PARENT:
                    stack.pop();
                    if(!lexer.match(Lexer.RP)){
                        parseError();
                    }
                    lexer.advance();
                    break;
            }
        }
        System.out.println("PDA accept");
    }

    private void parseError() {
        System.err.println("PDA parse error");
        System.exit(1);
    }

}
